package org.database.advice;

import com.mybatisflex.core.paginate.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.database.annotation.ProtectedField;
import org.database.service.StorageAbstract;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Field;
import java.util.Collection;
import java.util.List;
import java.util.Objects;

/**
 * DecryptAdvice
 *
 * @author grl
 * @date 2024/1/9
 */
@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
@Order(Ordered.HIGHEST_PRECEDENCE)
@SuppressWarnings("unchecked")
public class DecryptAdvice {
    private final StorageAbstract storageAbstract;

    @Pointcut("@annotation(org.database.annotation.NeedDecrypt)")
    public void pointCut() {
    }

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        Object proceed = joinPoint.proceed();
        if (proceed != null) {
            decrypt(proceed);
        }
        return proceed;
    }

    public void decrypt(Object proceed) {
        if (proceed instanceof String) {
            decryptValue(proceed);
        } else if (proceed instanceof Collection<?> collection) {
            decryptCollection(collection);
        } else if (proceed instanceof Page<?> page) {
            List<?> records = page.getRecords();
            decryptCollection(records);
        } else {
            encryptObject(proceed);
        }
    }

    /**
     * 加密对象
     *
     * @param proceed
     * @throws IllegalAccessException
     */
    private void encryptObject(Object proceed) {
        if (Objects.isNull(proceed)) {
            log.info("当前需要加密的object为null");
            return;
        }
        proceedObj(proceed);
    }

    private void proceedObj(Object proceed) {
        Field[] fields = proceed.getClass().getDeclaredFields();
        for (Field field : fields) {
            boolean containEncryptField = field.isAnnotationPresent(ProtectedField.class);
            if (containEncryptField) {
                //获取访问权
                try {
                    field.setAccessible(true);
                    String encrypt = storageAbstract.decrypt(proceed, String.valueOf(field.get(proceed)));
                    if (encrypt != null) {
                        field.set(proceed, encrypt);
                    }
                } catch (IllegalAccessException e) {
                    log.error("反射获取访问权失败！解密数据失败 属性{} ", field.getName(), e);
                }
            }
        }
    }

    /**
     * 集合
     *
     * @param collection
     */
    private void decryptCollection(Collection collection) {
        if (!CollectionUtils.isEmpty(collection)) {
            collection.forEach(this::proceedObj);
        }
    }

    /**
     * 加密单个值
     *
     * @param proceed
     * @return
     */
    public String decryptValue(Object proceed) {
        try {
            String encrypt = storageAbstract.decrypt(proceed, String.valueOf(proceed));
            return String.valueOf(encrypt);
        } catch (Exception e) {
            log.info("加密异常={}", e.getMessage());
        }
        return String.valueOf(proceed);
    }

}
